package com.appointment.pay.service.impl;

import java.io.InputStream;
import java.math.BigDecimal;
import java.util.Date;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;

import org.apache.commons.lang.StringUtils;
import org.apache.http.Consts;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import com.appointment.pay.core.entity.UserAccount;
import com.appointment.pay.core.entity.WxPayTransactionHistory;
import com.appointment.pay.core.enums.TradeStateEnum;
import com.appointment.pay.core.mapper.UserAccountMapper;
import com.appointment.pay.core.mapper.WxPayTransactionHistoryMapper;
import com.appointment.pay.service.IWxPayTransactionService;
import com.appointment.pay.util.MD5Util;
import com.appointment.pay.util.SignUtils;
import com.appointment.pay.util.XMLUtil;

@Service
public class WxPayTransactionServiceImpl implements IWxPayTransactionService {
    
    private static Logger logger = LoggerFactory.getLogger(WxPayTransactionServiceImpl.class);
    
    @Autowired
    private WxPayTransactionHistoryMapper wxpayTransactionMapper;
    
    @Autowired
    private UserAccountMapper userAccountMapper;
    
    private static final String WX_ORDER_QUERY_URL = "https://api.mch.weixin.qq.com/pay/orderquery";//微信查单接口
    
    private static final String WX_ORDER_CLOSE_URL = "https://api.mch.weixin.qq.com/pay/closeorder";//微信关单接口
    
    @Autowired
    private StringRedisTemplate redisTemplate;
    
    /**
     * 给用户账户充值
     * @param tran
     */
    private void updateUserAccount(WxPayTransactionHistory tran){
        Integer userId = tran.getUserId();
        Integer totalFee = tran.getTotalFee();//充值金额，单位为分
        UserAccount userAccount = userAccountMapper.queryByUserId(userId);
        BigDecimal inFee = new BigDecimal(totalFee.intValue());
        if(userAccount != null){
            BigDecimal amount = userAccount.getAmount();
            BigDecimal newAmount = amount.add(inFee);
            userAccount.setAmount(newAmount);
            BigDecimal totalRecharge = userAccount.getTotalRecharge();
            BigDecimal newTotalRecharge = totalRecharge.add(inFee);
            userAccount.setTotalRecharge(newTotalRecharge);
            userAccount.setUpdateTime(new Date());
            userAccountMapper.updateUserAccount(userAccount);
        }else{
            userAccount = new UserAccount();
            userAccount.setUserId(userId);
            userAccount.setAmount(inFee);//账户余额
            userAccount.setTotalRecharge(inFee);//累积充值
            userAccount.setCreateTime(new Date());
            userAccountMapper.insertUserAccount(userAccount);
        }
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED)
    public void queryWxpayOrder(String appid, String mch_id, String out_trade_no) {
        SortedMap<Object,Object> parameters = new TreeMap<Object,Object>();
        parameters.put("appid", appid);
        parameters.put("mch_id", mch_id);
        parameters.put("out_trade_no", out_trade_no);
        String nonce_str = MD5Util.MD5Encode(out_trade_no, "UTF-8");
        parameters.put("nonce_str", nonce_str);
        String key = redisTemplate.boundValueOps(mch_id).get();
        String sign = SignUtils.createSign("UTF-8", parameters, key);
        parameters.put("sign", sign);
        String requestStr = createXmlRequestStr(parameters);
        try{
            HttpClient client = new DefaultHttpClient();  
            HttpPost post = new HttpPost(WX_ORDER_QUERY_URL);  
            StringEntity entity = new StringEntity(requestStr,ContentType.create("application/xml", Consts.UTF_8));        
            post.setEntity(entity);  

            HttpResponse response = client.execute(post);  
            if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
                InputStream is = response.getEntity().getContent();  
                String result = XMLUtil.inStream2String(is);
                logger.info("微信支付查询订单接口返回信息==="+result);
                handleQueryResponse(result);
            }
        }catch(Exception ex){
            logger.error("调用微信支付查单接口异常!",ex);
            
        }
        
    }
    
    private String createXmlRequestStr(SortedMap<Object,Object> parameters){
        StringBuffer sb = new StringBuffer();
        sb.append("<xml>");
        sb.append("<appid>").append((String)parameters.get("appid")).append("</appid>");
        sb.append("<mch_id>").append((String)parameters.get("mch_id")).append("</mch_id>");
        sb.append("<out_trade_no>").append((String)parameters.get("out_trade_no")).append("</out_trade_no>");
        sb.append("<nonce_str>").append((String)parameters.get("nonce_str")).append("</nonce_str>");
        sb.append("<sign>").append((String)parameters.get("sign")).append("</sign>");
        sb.append("</xml>");
        logger.info("微信支付查询接口请求参数===="+sb.toString());
        return sb.toString();
    }
    
    private void handleQueryResponse(String result){
        if(StringUtils.isNotEmpty(result)){
            try{
                SortedMap<Object,Object> parameters = new TreeMap<Object,Object>();
                Map map = XMLUtil.doXMLParse(result);
                if(map != null){
                    //返回状态码
                    String returnCode = map.get("return_code").toString();
                    if("SUCCESS".equalsIgnoreCase(returnCode)){
                        //业务结果
                        parameters.put("return_code", returnCode);
                        if(map.containsKey("return_msg")){
                            parameters.put("return_msg", (String)map.get("return_msg"));
                        }
                        String appid = (String)map.get("appid");
                        parameters.put("appid", appid);
                        String mch_id  = (String)map.get("mch_id");
                        parameters.put("mch_id", mch_id);
                        String nonce_str  = (String)map.get("nonce_str");
                        parameters.put("nonce_str", nonce_str);
                        String sign  = (String)map.get("sign");
                        String result_code  = (String)map.get("result_code");
                        parameters.put("result_code", result_code);
                        if(map.containsKey("err_code")){
                            String err_code  = (String)map.get("err_code");
                            parameters.put("err_code", err_code);
                        }
                        if(map.containsKey("err_code_des")){
                            String err_code_des  = (String)map.get("err_code_des");
                            parameters.put("err_code_des", err_code_des);
                        }
                        if("SUCCESS".equalsIgnoreCase(result_code)){
                            if(map.containsKey("device_info")){
                                String device_info  = (String)map.get("device_info");
                                parameters.put("device_info", device_info);
                            }
                            String openid = (String)map.get("openid");
                            parameters.put("openid", openid);
                            if(map.containsKey("is_subscribe")){
                                String is_subscribe  = (String)map.get("is_subscribe");
                                parameters.put("is_subscribe", is_subscribe);
                            }
                            String trade_type = (String)map.get("trade_type");
                            parameters.put("trade_type", trade_type);
                            String trade_state = (String)map.get("trade_state");
                            parameters.put("trade_state", trade_state);
                            String bank_type = (String)map.get("bank_type");
                            parameters.put("bank_type", bank_type);
                            if(map.containsKey("total_fee")){
                                Integer total_fee = Integer.parseInt(map.get("total_fee").toString());
                                parameters.put("total_fee", total_fee);
                            }
                            
                            if(map.containsKey("fee_type")){
                                String fee_type  = (String)map.get("fee_type");
                                parameters.put("fee_type", fee_type);
                            }
                            if(map.containsKey("cash_fee")){
                                Integer cash_fee = Integer.parseInt(map.get("cash_fee").toString());
                                parameters.put("cash_fee", cash_fee);
                            }
                            
                            if(map.containsKey("cash_fee_type")){
                                String cash_fee_type  = (String)map.get("cash_fee_type");
                                parameters.put("cash_fee_type", cash_fee_type);
                            }
                            if(map.containsKey("coupon_fee")){
                                Integer coupon_fee  = (Integer)map.get("coupon_fee");
                                parameters.put("coupon_fee", coupon_fee);
                            }
                            if(map.containsKey("coupon_count")){
                                Integer coupon_count  = (Integer)map.get("coupon_count");
                                parameters.put("coupon_count", coupon_count);
                                if(coupon_count != null && coupon_count.intValue() > 0){
                                    for(int i=0;i<coupon_count.intValue();i++){
                                        if(map.containsKey("coupon_batch_id_"+i)){
                                            String coupon_batch_id  = (String)map.get("coupon_batch_id_"+i);
                                            parameters.put("coupon_batch_id_"+i, coupon_batch_id);
                                        }
                                        if(map.containsKey("coupon_id_"+i)){
                                            String coupon_id  = (String)map.get("coupon_id_"+i);
                                            parameters.put("coupon_id_"+i, coupon_id);
                                        }
                                        if(map.containsKey("coupon_fee_"+i)){
                                            Integer coupon_fee  = (Integer)map.get("coupon_fee_"+i);
                                            parameters.put("coupon_fee_"+i, coupon_fee);
                                        }
                                    }
                                }
                            }
                            
                            String transaction_id = (String)map.get("transaction_id");
                            parameters.put("transaction_id", transaction_id);
                            String out_trade_no = (String)map.get("out_trade_no");
                            parameters.put("out_trade_no", out_trade_no);
                            if(map.containsKey("attach")){
                                String attach = (String)map.get("attach");
                                parameters.put("attach", attach);
                            }
                            String time_end = (String)map.get("time_end");
                            parameters.put("time_end", time_end);
                            String trade_state_desc = (String)map.get("trade_state_desc");
                            parameters.put("trade_state_desc", trade_state_desc);
                            String key = redisTemplate.boundValueOps(mch_id).get();
                            String responseSign = SignUtils.createSign("UTF-8", parameters, key);//结果参数签名
                            if(sign.equalsIgnoreCase(responseSign)){
                                //处理查询状态
                                handleTradeStatus(trade_state,out_trade_no);
                            }else{
                                //查询结果签名不正确
                                logger.error("查询结果签名不正确！");
                            }
                        }
                    
                    }else{
                        logger.info("接口返回return_code=="+returnCode);
                    }
                }
            }catch(Exception ex){
                logger.error("处理微信查单结果信息异常！",ex);
            }
            
        }
    }
    
    private void handleTradeStatus(String trade_state,String out_trade_no){
        logger.info("接口返回支付状态trade_state=="+trade_state);
        WxPayTransactionHistory tran = wxpayTransactionMapper.queryByOutTradeNo(out_trade_no);
        if("SUCCESS".equalsIgnoreCase(trade_state)){
            //支付成功           
            if(TradeStateEnum.NOTPAY.getCode().equalsIgnoreCase(tran.getTradeState())){
                tran.setTradeState(TradeStateEnum.SUCCESS.getCode());
                tran.setState(1);
                tran.setStateName(TradeStateEnum.SUCCESS.getMessage());
                tran.setCorrectTime(new Date());
                wxpayTransactionMapper.updateByOutTradeNo(tran);
                //用户账户加钱
                updateUserAccount(tran);
            }
        }else if("REFUND".equalsIgnoreCase(trade_state)){
            //转入退款 
            tran.setTradeState(TradeStateEnum.REFUND.getCode());
            tran.setState(2);
            tran.setStateName(TradeStateEnum.REFUND.getMessage());
            tran.setCorrectTime(new Date());
            wxpayTransactionMapper.updateByOutTradeNo(tran);
        }else if("NOTPAY".equalsIgnoreCase(trade_state)){
            //未支付
            //do nothing...
        }else if("CLOSED".equalsIgnoreCase(trade_state)){
            //已关闭 
            tran.setTradeState(TradeStateEnum.CLOSED.getCode());
            tran.setState(3);
            tran.setStateName(TradeStateEnum.CLOSED.getMessage());
            tran.setCorrectTime(new Date());
            wxpayTransactionMapper.updateByOutTradeNo(tran);
        }else if("REVOKED".equalsIgnoreCase(trade_state)){
            //已撤销
            tran.setTradeState(TradeStateEnum.REVOK.getCode());
            tran.setState(4);
            tran.setStateName(TradeStateEnum.REVOK.getMessage());
            tran.setCorrectTime(new Date());
            wxpayTransactionMapper.updateByOutTradeNo(tran);
        }else if("USERPAYING".equalsIgnoreCase(trade_state)){
            //用户支付中
          //do nothing...
        }else if("PAYERROR".equalsIgnoreCase(trade_state)){
            //支付失败
            tran.setTradeState(TradeStateEnum.FAIL.getCode());
            tran.setState(5);
            tran.setStateName(TradeStateEnum.FAIL.getMessage());
            tran.setCorrectTime(new Date());
            wxpayTransactionMapper.updateByOutTradeNo(tran);
        }
    }

    @Override
    public void closeWxpayOrder(String appid, String mch_id, String out_trade_no) {
        SortedMap<Object,Object> parameters = new TreeMap<Object,Object>();
        parameters.put("appid", appid);
        parameters.put("mch_id", mch_id);
        parameters.put("out_trade_no", out_trade_no);
        String nonce_str = MD5Util.MD5Encode(out_trade_no, "UTF-8");
        parameters.put("nonce_str", nonce_str);
        String key = redisTemplate.boundValueOps(mch_id).get();
        String sign = SignUtils.createSign("UTF-8", parameters, key);
        parameters.put("sign", sign);
        String requestStr = createXmlRequestStr(parameters);
        try{
            HttpClient client = new DefaultHttpClient();  
            HttpPost post = new HttpPost(WX_ORDER_CLOSE_URL);  
            StringEntity entity = new StringEntity(requestStr,ContentType.create("application/xml", Consts.UTF_8));        
            post.setEntity(entity);  

            HttpResponse response = client.execute(post);  
            if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
                InputStream is = response.getEntity().getContent();  
                String result = XMLUtil.inStream2String(is);
                logger.info("微信支付关闭订单接口返回信息==="+result);
                handleCloseResponse(result,out_trade_no);
            }
        }catch(Exception ex){
            logger.error("调用微信支付查单接口异常!",ex);
            
        }
    }
    
    private void handleCloseResponse(String result,String out_trade_no){
        if(StringUtils.isNotEmpty(result)){
            try{
                SortedMap<Object,Object> parameters = new TreeMap<Object,Object>();
                Map map = XMLUtil.doXMLParse(result);
                if(map != null){
                    //返回状态码
                    String returnCode = map.get("return_code").toString();
                    if("SUCCESS".equalsIgnoreCase(returnCode)){
                        //业务结果
                        parameters.put("return_code", returnCode);
                        if(map.containsKey("return_msg")){
                            parameters.put("return_msg", (String)map.get("return_msg"));
                        }
                        String appid = (String)map.get("appid");
                        parameters.put("appid", appid);
                        String mch_id  = (String)map.get("mch_id");
                        parameters.put("mch_id", mch_id);
                        String nonce_str  = (String)map.get("nonce_str");
                        parameters.put("nonce_str", nonce_str);
                        String sign  = (String)map.get("sign");
                        String result_code  = null;
                        if(map.containsKey("result_code")){
                            result_code = (String)map.get("result_code");
                            parameters.put("result_code", result_code);
                        }
                       
                        if(map.containsKey("err_code")){
                            String err_code  = (String)map.get("err_code");
                            parameters.put("err_code", err_code);
                        }
                        if(map.containsKey("err_code_des")){
                            String err_code_des  = (String)map.get("err_code_des");
                            parameters.put("err_code_des", err_code_des);
                        }
                        String key = redisTemplate.boundValueOps(mch_id).get();
                        String responseSign = SignUtils.createSign("UTF-8", parameters, key);//结果参数签名
                        if(sign.equalsIgnoreCase(responseSign)){
                            //处理响应结果
                            if("SUCCESS".equalsIgnoreCase(result_code)){
                                //成功，修改订单状态
                                WxPayTransactionHistory tran = wxpayTransactionMapper.queryByOutTradeNo(out_trade_no);
                                tran.setTradeState(TradeStateEnum.CLOSED.getCode());
                                tran.setState(3);
                                tran.setStateName(TradeStateEnum.CLOSED.getMessage());
                                tran.setCorrectTime(new Date());
                                wxpayTransactionMapper.updateByOutTradeNo(tran);
                            }
                            
                        }else{
                            //查询结果签名不正确
                            logger.error("微信关单接口返回结果签名不正确！");
                        }
                    
                    }else{
                        logger.info("接口返回return_code=="+returnCode);
                    }
                }
            }catch(Exception ex){
                logger.error("处理微信关单接口结果信息异常！",ex);
            }
            
        }
    }
    
}
